using System;
using System.Collections.Generic;
using System.Reflection;
#if TEST
using Fadd.Tests;
using Xunit;
#endif

namespace Fadd.Components
{
    /// <summary>
    /// Used to create and maintain components.
    /// </summary>
    /// <remarks>
    /// Supports lazy loading which means that components will be loaded when 
    /// requested. 
    /// </remarks>
    public class ComponentFactory : IComponentManager
    {
        private readonly Dictionary<Type, TypeInformation> _instances = new Dictionary<Type, TypeInformation>();
        private readonly Dictionary<string, object> _nameIndex = new Dictionary<string, object>();
        private readonly Dictionary<Type, TypeInformation> _notLoaded = new Dictionary<Type, TypeInformation>();
        private readonly Dictionary<Type, TypeInformation> _privateInstances = new Dictionary<Type, TypeInformation>();

        /// <summary>
        /// We failed to find all constructor parameters for a certain type. You can use
        /// these event to supply the missing parameters.
        /// </summary>
        public event EventHandler<ParametersRequestedEventArgs> ParametersRequested = delegate { };

        #region class TypeInformation

        private class TypeInformation
        {
            private readonly List<Type> _dependencies = new List<Type>();
            private readonly Action<object> _onCreatedHandler;
            private readonly Dictionary<string, object> _parameters;
            private readonly bool _private;
            private readonly Type _type;
            private object _instance;
            private string _name;

            public TypeInformation(Type type, IEnumerable<Parameter> parameters, bool isPrivate,
                                   Action<object> onCreatedHandler)
            {
                _type = type;
                _parameters = new Dictionary<string, object>();
                foreach (Parameter pair in parameters)
                    _parameters.Add(pair.Name, pair.Value);
                _private = isPrivate;
                _onCreatedHandler = onCreatedHandler;
            }

            public TypeInformation(Type type, object instance, bool isPrivate)
            {
                _type = type;
                _parameters = new Dictionary<string, object>();
                _instance = instance;
                _private = isPrivate;
            }

            public Dictionary<string, object> Parameters
            {
                get { return _parameters; }
            }

            /// <summary>
            /// Instance type
            /// </summary>
            public Type Type
            {
                get { return _type; }
            }

            /// <summary>
            /// Other components that this one is dependent of.
            /// </summary>
            /// <remarks>
            /// Usually used when shutting down components (to be able to shut them down in
            /// the correct order).
            /// </remarks>
            public List<Type> Dependencies
            {
                get { return _dependencies; }
            }

            /// <summary>
            /// Gets a handler to be invoked upon the creation of the component
            /// </summary>
            public Action<object> OnCreated
            {
                get { return _onCreatedHandler; }
            }

            public object Instance
            {
                get { return _instance; }
                internal set { _instance = value; }
            }

            public bool Private
            {
                get { return _private; }
            }

            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }

            public override string ToString()
            {
                if (_instance == null)
                    return Type.ToString();

                return Type + " (" + _instance + ")";
            }
        }

        #endregion

        /// <summary>
        /// Get all components that have not been created yet.
        /// </summary>
        /// <returns>I list of all not created components.</returns>
        public IEnumerable<Type> NotCreated
        {
            get
            {
                List<Type> notLoaded = new List<Type>();
                foreach (KeyValuePair<Type, TypeInformation> pair in _notLoaded)
                    notLoaded.Add(pair.Value.Type);

                return notLoaded;
            }
        }

        #region IComponentManager Members

        /// <summary>
        /// Add a component.
        /// </summary>
        /// <typeparam name="InterfaceType">Interface type, used to request components.</typeparam>
        /// <typeparam name="InstanceType">Instance type, used to create component.</typeparam>
        /// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        public void Add<InterfaceType, InstanceType>(params Parameter[] constructorParameters)
        {
            AddType(typeof (InterfaceType), typeof (InstanceType), constructorParameters);
        }

        /// <summary>
        /// Add an component
        /// </summary>
		/// <param name="interfaceType">Interface type, used to request components.</param>
		/// <param name="instanceType">Instance type, used to create component.</param>
        /// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        /// <exception cref="ArgumentException">If <paramref name="interfaceType"/> is not an interface, or if <paramref name="instanceType"/> is not an implementation of the interface.</exception>
        public void AddType(Type interfaceType, Type instanceType, params Parameter[] constructorParameters)
        {
            if (instanceType.IsInterface)
                throw new ArgumentException(instanceType + " cannot be an interface.", "instanceType");
            if (!interfaceType.IsAssignableFrom(instanceType))
                throw new ArgumentException(
                    interfaceType.FullName + " must be assigneble from " + instanceType.FullName, "interfaceType");
            if (_instances.ContainsKey(interfaceType))
                throw new ArgumentException("Interface have already been mapped to: " + _instances[interfaceType],
                                            "interfaceType");

            lock (_notLoaded)
            {
                if (_notLoaded.ContainsKey(interfaceType))
                    throw new ArgumentException("Interface have already been mapped to: " + _notLoaded[interfaceType],
                                                "interfaceType");
                _notLoaded.Add(interfaceType, CreateTypeInfo(instanceType, constructorParameters, false));
            }
        }

        /// <summary>
        /// Add an existing instance.
        /// </summary>
        /// <param name="instance"></param>
        /// <typeparam name="T">Interface type being requested when getting this component</typeparam>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// <see cref="AddInstance{T}"/> will also invoke the Created event.
        /// </remarks>
        public void AddInstance<T>(object instance)
        {
            AddInstance(typeof (T), instance);
        }

        /// <summary>
        /// Add an existing instance.
        /// </summary>
        /// <param name="interfaceType">The type the instance should be added as</param>
        /// <param name="instance">The instance to add</param>
        /// <remarks>
        /// <see cref="AddInstance"/> will also invoke the Created event.
        /// </remarks>
        /// <exception cref="ArgumentException">If <paramref name="interfaceType"/> is not assignable from <paramref name="instance"/> type..</exception>
        public void AddInstance(Type interfaceType, object instance)
        {
            if (!interfaceType.IsAssignableFrom(instance.GetType()))
                throw new ArgumentException(interfaceType.FullName + " is not assignable from " +
                                            instance.GetType().FullName);
            if (_notLoaded.ContainsKey(interfaceType))
                throw new ArgumentException("Interface have already been mapped to: " + _notLoaded[interfaceType]);

            lock (_instances)
            {
                if (_instances.ContainsKey(interfaceType))
                    throw new ArgumentException("Interface have already been mapped to: " + _instances[interfaceType]);

                TypeInformation typeInformation = new TypeInformation(interfaceType, instance, false);
                CheckComponentName(instance.GetType(), typeInformation, instance);
                _instances.Add(interfaceType, typeInformation);
                Created(this, new CreatedEventArgs(interfaceType, instance, CreateMode.InstanceAdded));
            }
        }

        /// <summary>
        /// Checks whether a specific type exists (loaded or not loaded)
        /// </summary>
        /// <param name="interfaceType">Interface to check.</param>
        public bool Contains(Type interfaceType)
        {
            return _notLoaded.ContainsKey(interfaceType) || _instances.ContainsKey(interfaceType);
        }

        /// <summary>
        /// Checks whether a specific type exists (loaded or not loaded)
        /// </summary>
        /// <param name="name">Name of component.</param>
        public bool Contains(string name)
        {
            return _nameIndex.ContainsKey(name);
        }

        /// <summary>
        /// Checks whether a certain component is created or not.
        /// </summary>
        /// <param name="interfaceType">Interface to check</param>
        /// <returns>true if component is created.</returns>
        public bool IsCreated(Type interfaceType)
        {
            return _instances.ContainsKey(interfaceType);
        }

        /// <summary>
        /// Gets if a component have been created or not.
        /// </summary>
        /// <param name="name">Component name.</param>
        /// <returns>true if component have been created.</returns>
        public bool IsCreated(string name)
        {
            object instance;
            if (!_nameIndex.TryGetValue(name, out instance))
                return false;
            return instance != null;
        }

        /// <summary>
        /// Get a component.
        /// </summary>
        /// <remarks>
        /// The component will be created if needed.
        /// </remarks>
        /// <typeparam name="T">Type of component (interface)</typeparam>
        /// <returns>Component if found; otherwise null.</returns>
        public T Get<T>() where T : class
        {
            return (T) Get(typeof (T));
        }

        /// <summary>
        /// Get a component
        /// </summary>
        /// <param name="interfaceType">Type of component to get.</param>
        /// <returns>Component if found; otherwise null.</returns>
        public object this[Type interfaceType]
        {
            get { return Get(interfaceType); }
        }

        /// <summary>
        /// Gets a component
        /// </summary>
        /// <param name="name">Name of component to get.</param>
        /// <returns>Component if found; otherwise null.</returns>
        object IComponentManager.this[string name]
        {
            get
            {
                object instance;
                return _nameIndex.TryGetValue(name, out instance) ? instance : null;
            }
        }

        #endregion

        #region AddPrivate methods

        /// <summary>
        /// Add a private component.
        /// </summary>
        /// <typeparam name="InterfaceType">Interface type, used to request components.</typeparam>
        /// <typeparam name="InstanceType">Instance type, used to create component.</typeparam>
        /// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        /// <remarks>
        /// 	<para>
        /// Private components works as regular components, the only difference is that they are not
        /// accessible from outside.
        /// </para>
        /// 	<para>
        /// Private components are typically used to instantiate components that should not be accessible
        /// from anywhere, but they use public components must therefore be loaded when all dependencies
        /// have been met.
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentException">
        /// If: 
        /// <list type="bullet">
        ///     <item>The <typeparamref name="InstanceType"/> is an interface (which obviously cannot be instantiated)</item>
        ///     <item>The <typeparamref name="InstanceType"/> isn't assignable to <typeparamref name="InterfaceType"/></item>
        ///     <item>The <typeparamref name="InterfaceType"/> has already been mapped to another instance</item>
        /// </list>
        /// </exception>
        public void AddPrivate<InterfaceType, InstanceType>(params Parameter[] constructorParameters)
        {
            AddPrivate<InterfaceType, InstanceType>(null, constructorParameters);
        }

        /// <summary>
        /// Add a private component.
        /// </summary>
		/// <param name="interfaceType">Interface type, used to request components.</param>
		/// <param name="instanceType">Instance type, used to create component.</param>
        /// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        /// <remarks>
        /// 	<para>
        /// Private components works as regular components, the only difference is that they are not
        /// accessible from outside.
        /// </para>
        /// 	<para>
        /// Private components are typically used to instantiate components that should not be accessible
        /// from anywhere, but they use public components must therefore be loaded when all dependencies
        /// have been met.
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentException">
        /// <list type="bullet">
        ///     <item>The <paramref name="instanceType"/> is an interface (which obviously cannot be instantiated)</item>
        ///     <item>The <paramref name="instanceType"/> isn't assignable to <paramref name="interfaceType"/></item>
        ///     <item>The <paramref name="interfaceType"/> has already been mapped to another instance</item>
        /// </list>
        /// </exception>
        public void AddPrivate(Type interfaceType, Type instanceType, params Parameter[] constructorParameters)
        {
            if (instanceType.IsInterface)
                throw new ArgumentException(instanceType + " cannot be an interface.");
            if (!interfaceType.IsAssignableFrom(instanceType))
                throw new ArgumentException(interfaceType.FullName + " must be assignable from " + instanceType.FullName);
            if (_instances.ContainsKey(interfaceType))
                throw new ArgumentException("Interface have already been mapped to: " + _instances[interfaceType]);
            if (_notLoaded.ContainsKey(interfaceType))
                throw new ArgumentException("Interface have already been mapped to public: " + _notLoaded[interfaceType]);

            lock (_notLoaded)
            {
                _notLoaded.Add(interfaceType, CreateTypeInfo(instanceType, constructorParameters, true, null));
            }
        }

        /// <summary>
        /// Add a private component.
        /// </summary>
        /// <typeparam name="InterfaceType">Interface type, used to request components.</typeparam>
        /// <typeparam name="InstanceType">Instance type, used to create component.</typeparam>
        /// <param name="componentCreated">A delegate to be invoked when the private component has been instantiated, will be passed the interface of the created component</param>
        /// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        /// <remarks>
        /// 	<para>
        /// Private components are created like regular components when their dependencies are met,
        /// the difference between private and regular components are that they are not accessible from anywhere.
        /// </para>
        /// 	<para>
        /// Private components are typically used to instantiate components that are not used anywhere
        /// but still need to be created when their dependencies have been met.
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentException">
        /// If: 
        /// <list type="bullet">
        ///     <item>The <typeparamref name="InstanceType"/> is an interface (which obviously cannot be instantiated)</item>
        ///     <item>The <typeparamref name="InstanceType"/> isn't assignable to <typeparamref name="InterfaceType"/></item>
        ///     <item>The <typeparamref name="InterfaceType"/> has already been mapped to another instance</item>
        /// </list>
        /// </exception>
        public void AddPrivate<InterfaceType, InstanceType>(Action<object> componentCreated,
                                                            params Parameter[] constructorParameters)
        {
            Type interfaceType = typeof (InterfaceType);
            Type instanceType = typeof (InstanceType);
            if (instanceType.IsInterface)
                throw new ArgumentException(instanceType + " cannot be an interface.");
            if (!interfaceType.IsAssignableFrom(instanceType))
                throw new ArgumentException(interfaceType.FullName + " must be assignable from " + instanceType.FullName);
            if (_instances.ContainsKey(interfaceType))
                throw new ArgumentException("Interface have already been mapped to: " + _instances[interfaceType]);
            if (_notLoaded.ContainsKey(interfaceType))
                throw new ArgumentException("Interface have already been mapped to public: " + _notLoaded[interfaceType]);

            lock (_notLoaded)
            {
                _notLoaded.Add(interfaceType,
                               CreateTypeInfo(instanceType, constructorParameters, true, componentCreated));
            }
        }

        #endregion

        /// <summary>
        /// Invoked when a component is missing.
        /// </summary>
        public event EventHandler<RequestComponentEventArgs> ComponentMissing = delegate { };

        private TypeInformation CreateTypeInfo(Type instanceType, IEnumerable<Parameter> constructorParameters,
                                               bool isPrivate, Action<object> onComponentCreated)
        {
            TypeInformation info = new TypeInformation(instanceType, constructorParameters, isPrivate,
                                                       onComponentCreated);
            CheckComponentName(instanceType, info, null);
            return info;
        }

        private TypeInformation CreateTypeInfo(Type instanceType, IEnumerable<Parameter> constructorParameters,
                                               bool isPrivate)
        {
            return CreateTypeInfo(instanceType, constructorParameters, isPrivate, null);
        }

        private void CheckComponentName(ICustomAttributeProvider instanceType, TypeInformation info, object instance)
        {
            object[] attributes = instanceType.GetCustomAttributes(typeof (ComponentNameAttribute), true);
            if (attributes.Length != 1) return;
            info.Name = ((ComponentNameAttribute) attributes[0]).Name;
            _nameIndex.Add(info.Name, instance);
        }

#if TEST
        [Fact]
        private void TestAddType()
        {
            AddType(typeof (IPropertyTestObj2), typeof (PropertyTestObj2));
            Get<IPropertyTestObj2>();
        }
#endif

        /// <summary>
        /// Loads a component and adds it to <see cref="_instances"/> if successful
        /// </summary>
        /// <param name="interfaceType">Type what user will request</param>
        /// <param name="typeInfo">Type to create</param>
        /// <param name="dependenceyStack">Dependency stack, used to find circular dependencies. Type should be <paramref name="interfaceType"/></param>
        /// <returns>Instance if created successfully; otherwise false.</returns>
        /// <exception cref="CircularDependenciesException">If a circular dependency was detected.</exception>
        /// <exception cref="InvalidOperationException">If a component have already been loaded.</exception>
        /// <exception cref="Exception">If a component throws any exception during creation.</exception>
        private object LoadComponent(Type interfaceType, TypeInformation typeInfo, Stack<Type> dependenceyStack)
        {
            // remove component to avoid loop on cyclic dependencies.
            if (dependenceyStack.Contains(interfaceType))
                throw new CircularDependenciesException(dependenceyStack);
            dependenceyStack.Push(interfaceType);

            // Instance have already been loaded, but with another registered interface.
            lock (_instances)
            {
                foreach (KeyValuePair<Type, TypeInformation> pair in _instances)
                {
                    if (pair.Value.Instance.GetType() != typeInfo.Type) continue;
                    if (_instances.ContainsKey(interfaceType))
                        throw new InvalidOperationException("A component has already been loaded to the interface: " +
                                                            interfaceType.Name);

                    _instances.Add(interfaceType, pair.Value);
                    _notLoaded.Remove(interfaceType);
                    dependenceyStack.Pop();
                    return pair.Value.Instance;
                }
            }

            List<object> args = new List<object>();
            ConstructorInfo constructor = FindConstructor(interfaceType, typeInfo, args, dependenceyStack);

            // Failed to find an appropiate constructor with the specified parameters.
            // try find additional parameters using the event.
            if (constructor == null && typeInfo.Parameters.Count == 0)
            {
                ParametersRequestedEventArgs customArgs = new ParametersRequestedEventArgs(interfaceType, typeInfo.Type);
                ParametersRequested(this, customArgs);
                if (customArgs.Parameters.Count != 0)
                {
                    foreach (KeyValuePair<string, object> pair in customArgs.Parameters)
                        typeInfo.Parameters.Add(pair.Key, pair.Value);
                }

                // and try again.
				constructor = FindConstructor(interfaceType, typeInfo, args, dependenceyStack);
            }

            // Create instance.
            //todo: document/catch activator exceptions.
            if (constructor != null)
            {
                object instance;

                try
                {
                    instance = Activator.CreateInstance(typeInfo.Type, args.ToArray());
                }
                catch (TargetInvocationException e)
                {
                    FieldInfo remoteStackTraceString =
                        typeof (Exception).GetField("_remoteStackTraceString",
                                                    BindingFlags.Instance | BindingFlags.NonPublic);
                    remoteStackTraceString.SetValue(e.InnerException, e.InnerException.StackTrace + Environment.NewLine);
                    throw e.InnerException;
                }

                lock (_instances)
                {
                    if (!_instances.ContainsKey(interfaceType))
                    {
                        typeInfo.Instance = instance;
                        _instances.Add(interfaceType, typeInfo);
                    }
                }
                lock (_notLoaded)
                    _notLoaded.Remove(interfaceType);

                Created(this, new CreatedEventArgs(interfaceType, instance, CreateMode.Created, dependenceyStack));

                if (typeInfo.OnCreated != null)
                    typeInfo.OnCreated(instance);

                dependenceyStack.Pop();
                return instance;
            }

            dependenceyStack.Pop();
            return null;
        }

		/// <exception cref="MissingConstructorException"><c>MissingParametersException</c>.</exception>
        /// <exception cref="CircularDependenciesException"><c>CircularDependenciesException</c>.</exception>
        private ConstructorInfo FindConstructor(Type interfaceType, TypeInformation typeInfo, ICollection<object> args,
                                                Stack<Type> dependencyStack)
        {
            Check.Require(typeInfo, "typeInfo");

            Dictionary<ConstructorInfo, ParameterInfo> missingParameters =
                new Dictionary<ConstructorInfo, ParameterInfo>();
            List<List<Type>> constructorDependencies = new List<List<Type>>();
        	ConstructorInfo[] constructors = typeInfo.Type.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
			if (constructors.Length == 0)
				throw new MissingConstructorException(typeInfo.Type);

            foreach (ConstructorInfo constructor in typeInfo.Type.GetConstructors())
            {
                args.Clear();
                ParameterInfo[] pars = constructor.GetParameters();
                List<Type> dependencies = new List<Type>();
                foreach (ParameterInfo info in pars)
                {
                    // Check to see if this is one of the parameters supplied by the user.
                    if (typeInfo.Parameters.ContainsKey(info.Name) &&
                        info.ParameterType.IsAssignableFrom(typeInfo.Parameters[info.Name].GetType()))
                    {
                        args.Add(typeInfo.Parameters[info.Name]);
                        continue;
                    }

                    if (dependencyStack.Contains(info.ParameterType))
                        dependencies.Add(info.ParameterType);

                    object arg = Get(info.ParameterType, dependencyStack);
                    if (arg == null)
                    {
                        missingParameters.Add(constructor, info);
                        break; // no use to continue since we couldnt find the parameter.
                    }

                    typeInfo.Dependencies.Add(info.ParameterType);
                    args.Add(arg);
                }

                if (args.Count + dependencies.Count != pars.Length)
                    continue;

                if (dependencies.Count > 0)
                    constructorDependencies.Add(dependencies);
                else
                    return constructor;
            }

            if (constructorDependencies.Count > 0)
            {
                dependencyStack.Push(constructorDependencies[0][0]);
                throw new CircularDependenciesException(dependencyStack);
            }

			throw new MissingConstructorException(typeInfo.Type, typeInfo.Parameters, missingParameters);
        }

        /// <summary>
        /// Create all components that have not been created.
        /// </summary>
        /// <exception cref="LoadFailedException">If any of the components can't be loaded.</exception>
        public void CreateAll()
        {
            Dictionary<Type, Type> failed = new Dictionary<Type, Type>();
            lock (_notLoaded)
            {
                bool canRun = true;
                while (canRun)
                {
                    foreach (KeyValuePair<Type, TypeInformation> pair in _notLoaded)
                    {
                        if (failed.ContainsKey(pair.Key))
                        {
                            if (failed.Count != _notLoaded.Count)
                                continue;

                            canRun = false;
                            break;
                        }

                        object obj = LoadComponent(pair.Key, pair.Value, new Stack<Type>());
                        if (obj == null)
                            failed.Add(pair.Key, pair.Value.Type);

                        break;
                    }

                    if (_notLoaded.Count == 0)
                        break;
                }
            }

            if (failed.Count != 0)
                throw new LoadFailedException(failed);

            MovePrivateInstances();
        }

        /// <summary>
        /// Move all private instances from <see cref="_instances"/> to the <see cref="_privateInstances"/> dictionary.
        /// </summary>
        /// <remarks>
        /// locks <see cref="_instances"/> first, and then <see cref="_privateInstances"/>.
        /// </remarks>
        private void MovePrivateInstances()
        {
            lock (_instances)
            {
                lock (_privateInstances)
                {
                    List<Type> keys = new List<Type>();
                    foreach (KeyValuePair<Type, TypeInformation> pair in _instances)
                    {
                        if (!pair.Value.Private)
                            continue;
                        _privateInstances.Add(pair.Key, pair.Value);
                        keys.Add(pair.Key);
                    }

                    foreach (Type key in keys)
                        _instances.Remove(key);
                }
            }
        }

        /// <summary>
        /// Invoked when a component have been created (or if an instance have been added).
        /// </summary>
        public event EventHandler<CreatedEventArgs> Created = delegate { };

        /// <summary>
        /// Try to get a component.
        /// </summary>
        /// <param name="interfaceType">Type requested by users.</param>
        /// <returns>object if created; otherwise null.</returns>
        /// <remarks>
        /// should only be used by external components.
        /// </remarks>
        private object Get(Type interfaceType)
        {
            TypeInformation typeInformation;
            Type type = interfaceType;
            if (_instances.TryGetValue(type, out typeInformation))
                return typeInformation.Private ? null : typeInformation.Instance;

            return Get(interfaceType, new Stack<Type>());
        }

        /// <summary>
        /// Try to get a component, either loaded or not loaded. Will also try to find missing components.
        /// </summary>
        /// <param name="interfaceType">Type requested by users.</param>
        /// <param name="dependencyStack">Stack to be able to track circular dependencies.</param>
        /// <returns>object if created; otherwise null.</returns>
        private object Get(Type interfaceType, Stack<Type> dependencyStack)
        {
            TypeInformation typeInformation;
            Type type = interfaceType;
            if (_instances.TryGetValue(type, out typeInformation))
                return typeInformation.Private ? null : typeInformation.Instance;

            if (!_notLoaded.TryGetValue(type, out typeInformation))
            {
                RequestComponentEventArgs args = new RequestComponentEventArgs(interfaceType, dependencyStack);
                ComponentMissing(this, args);

                // We've got back an instance to create
                if (args.InstanceOrType is Type)
                {
                    AddType(args.InterfaceType, (Type) args.InstanceOrType);
                    _notLoaded.TryGetValue(type, out typeInformation);
                }
                else
                {
                    // we've got an instance. Don't add it or anything since 
                    // user have chosen not to include it.
                    return args.InstanceOrType;
                }
            }

            object instance = LoadComponent(type, typeInformation, dependencyStack);
            if (instance != null)
                return instance;

            Console.WriteLine("Missing: " + interfaceType.FullName);
            return null;


            // the component can have missing dependencies.
            //RequestComponentEventArgs args2 = new RequestComponentEventArgs(interfaceType, dependencyStack);
            //ComponentMissing(this, args2);
            //return args2.Instance;
        }

        /// <summary>
        /// Visit all components
        /// </summary>
        /// <param name="visitor">Delegate to invoke with the found components</param>
        /// <remarks>Does not visit private components</remarks>
        public void Visit(ComponentVisitor visitor)
        {
            Check.Require(visitor, "visitor");

            foreach (KeyValuePair<Type, TypeInformation> pair in _instances)
            {
                if (pair.Value.Private)
                    continue;

                visitor(pair.Key, pair.Value.Instance);
            }
        }

        /// <summary>
        /// Visits all components including private ones
        /// </summary>
        /// <remarks>
        /// Components being added during the visit will not be processed.
        /// </remarks>
        /// <param name="visitor">Delegate to invoke with the found components</param>
        public void VisitAll(ComponentVisitor visitor)
        {
            Check.Require(visitor, "visitor");

            Dictionary<Type, TypeInformation> instances = new Dictionary<Type, TypeInformation>(_privateInstances);
            foreach (KeyValuePair<Type, TypeInformation> pair in instances)
                visitor(pair.Key, pair.Value.Instance);

            instances = new Dictionary<Type, TypeInformation>(_instances);
            foreach (KeyValuePair<Type, TypeInformation> pair in instances)
                visitor(pair.Key, pair.Value.Instance);
        }
    }

    /// <summary>
    /// Used to visit all components.
    /// </summary>
    /// <param name="interfaceType">component interface.</param>
    /// <param name="instance">instance.</param>
    public delegate void ComponentVisitor(Type interfaceType, object instance);

    /// <summary>
    /// Arguments for <see cref="ComponentFactory.ComponentMissing"/>.
    /// </summary>
    public class RequestComponentEventArgs : EventArgs
    {
        private readonly Stack<Type> _dependencyStack;
        private readonly Type _interfaceType;
        private object _instance;

        /// <summary>
        /// Initializes a new instance of the <see cref="RequestComponentEventArgs"/> class.
        /// </summary>
        /// <param name="interfaceType">Requested component type.</param>
        /// <param name="dependencyStack">The dependency stack.</param>
        public RequestComponentEventArgs(Type interfaceType, Stack<Type> dependencyStack)
        {
            _interfaceType = interfaceType;
            _dependencyStack = dependencyStack;
        }

        /// <summary>
        /// A component instance or type of instance to create.
        /// </summary>
        /// <example>
        /// <code>
        /// void ComponentMissing(object source, RequestComponentEventArgs args)
        /// {
        ///     if (args.InterfaceType != typeof(IMyManager))
        ///         return;
        /// 
        /// #if TEST
        ///     args.InstanceOrType = new MyManagerStub();
        /// #else
        ///     args.InstanceOrType = typeof(MyManager);
        /// #endif    
        /// }
        /// </code>
        /// </example>
        public object InstanceOrType
        {
            get { return _instance; }
            set { _instance = value; }
        }

        /// <summary>
        /// Requested component type
        /// </summary>
        public Type InterfaceType
        {
            get { return _interfaceType; }
        }

        /// <summary>
        /// Dependency stack
        /// </summary>
        public Stack<Type> DependencyStack
        {
            get { return _dependencyStack; }
        }
    }
}